package com.anteater.eattheant.view;

import java.util.ArrayList;
import java.util.List;

import javax.microedition.khronos.opengles.GL10;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.opengl.GLUtils;

import com.anteater.eattheant.R;
import com.anteater.eattheant.view.ETASurfaceView.SoundListener;

public class Anthill extends Group {

	private final float SCALE_FACTOR;
	public static final float HEIGHT = 1.0f;
	public static final float WIDTH = 2.0f;

	private Maze maze;

	private int[][] distanceGraph;
	private int[][] tongueRoute;

	private List<Ant> deadAnts;

	private GL10 gl;
	private Context context;

	public Anthill(Maze maze, List<com.anteater.eattheant.util.Ant> ants,
			GL10 gl, Context context) {
		this.maze = maze;
		this.tongueRoute = new int[maze.getGraph().length][maze.getGraph()[0].length];
		this.deadAnts = new ArrayList<Ant>();

		SCALE_FACTOR = HEIGHT / maze.getGraph().length;

		this.gl = gl;
		this.context = context;

		insertAnts(ants);
		precalculateDistances();
	}

	public void updateAnts() {
		for (int i = 0; i < size(); i++) {
			Ant a = (Ant) get(i);
			a.walk();
		}
	}

	public List<Ant> getDeadAnts() {
		return deadAnts;
	}

	@Override
	public void draw(GL10 gl) {
		class Drawer {
			float h = Tunnel.HEIGHT * SCALE_FACTOR;
			float w = Tunnel.WIDTH * SCALE_FACTOR;

			void draw(GL10 gl, Ant ant) {
				gl.glPushMatrix();

				float ux = (float) (-WIDTH / 2.0f + ant.getJ() * w);
				float uy = (float) (HEIGHT / 2.0f - ant.getI() * h);
				float x = ux + w / 2.0f;
				float y = uy - h / 2.0f;

				gl.glTranslatef(x, y, 0);
				gl.glScalef(SCALE_FACTOR, SCALE_FACTOR, SCALE_FACTOR);

				ant.draw(gl);

				gl.glPopMatrix();
			}
		}

		Drawer dr = new Drawer();

		for (int i = 0; i < size(); i++) {
			dr.draw(gl, (Ant) get(i));
		}
		for (Ant ant : deadAnts) {
			dr.draw(gl, ant);
		}
	}

	private void insertAnts(List<com.anteater.eattheant.util.Ant> ants) {
		for (com.anteater.eattheant.util.Ant ant : ants) {
			add(new Ant(ant.getI(), ant.getJ()));
		}
	}

	private void removeAnts(Ant ant) {
		// TODO som para quando a formiga fugir
		remove(ant);
	}

	private void floodFill(int i, int j, int dist) {
		if (i < 0 || j < 0) {
			return;
		}
		if (i >= distanceGraph.length || j >= distanceGraph[i].length
				|| distanceGraph[i][j] < dist || tongueRoute[i][j] == 1) {
			return;
		}
		if (distanceGraph[i][j] > dist) {
			distanceGraph[i][j] = dist;
		}
		if ((maze.getGraph()[i][j] & Tunnel.TOP) > 0) {
			floodFill(i - 1, j, dist + 1);
		}
		if ((maze.getGraph()[i][j] & Tunnel.BOTTOM) > 0) {
			floodFill(i + 1, j, dist + 1);
		}
		if ((maze.getGraph()[i][j] & Tunnel.RIGHT) > 0) {
			floodFill(i, j + 1, dist + 1);
		}
		if ((maze.getGraph()[i][j] & Tunnel.LEFT) > 0) {
			floodFill(i, j - 1, dist + 1);
		}
		return;
	}

	private void precalculateDistances() {
		distanceGraph = new int[maze.getGraph().length][maze.getGraph()[0].length];
		for (int i = 0; i < distanceGraph.length; i++) {
			for (int j = 0; j < distanceGraph[i].length; j++) {
				distanceGraph[i][j] = Integer.MAX_VALUE;
			}
		}
		for (Escape escape : maze.getEscapes()) {
			floodFill(escape.getI(), escape.getJ(), 0);
		}
	}

	public class Ant extends Group {

		public static final float HEIGHT = 1.0f;
		public static final float WIDTH = 1.0f;

		private static final int SPEED = 1;
		private static final int SPEED_FRACTION = 60;
		private int movementCounter = 0;

		private int i;
		private int j;

		private Square texture;
		private int texfiles[];

		public Ant(int i, int j) {
			this.i = i;
			this.j = j;

			bindFiles();

			texture = new Square(1.0f);
			texture.setTexture(
					new float[] { 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f,
							1.0f },
					loadTexture(gl, maze.getGraph()[i][j]
							& -maze.getGraph()[i][j]));
			add(texture);
		}

		private void bindFiles() {
			texfiles = new int[32];
			texfiles[Tunnel.TOP] = R.drawable.ant_t_64x64;
			texfiles[Tunnel.RIGHT] = R.drawable.ant_r_64x64;
			texfiles[Tunnel.BOTTOM] = R.drawable.ant_b_64x64;
			texfiles[Tunnel.LEFT] = R.drawable.ant_l_64x64;
		}

		private int loadTexture(GL10 gl, int orientation) {
			int texture[] = new int[1];

			gl.glGenTextures(1, texture, 0);

			Bitmap bitmap = BitmapFactory.decodeResource(
					context.getResources(), texfiles[orientation]);

			gl.glBindTexture(GL10.GL_TEXTURE_2D, texture[0]);

			gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER,
					GL10.GL_NEAREST);
			gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER,
					GL10.GL_NEAREST);

			GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, GL10.GL_RGBA, bitmap,
					GL10.GL_UNSIGNED_BYTE, 0);

			bitmap.recycle();

			return texture[0];
		}

		public int getI() {
			return i;
		}

		public void setI(int i) {
			this.i = i;
		}

		public int getJ() {
			return j;
		}

		public void setJ(int j) {
			this.j = j;
		}

		public void walk() {
			movementCounter = (movementCounter + 1) % SPEED_FRACTION;

			if (movementCounter == 0) {
				if (distanceGraph[i][j] == 0) {
					removeAnts(this);
				} else {
					walk(nextStep(i, j));
				}
			}
		}

		private void walk(short direction) {
			if (direction > 0) {
				texture.setTexture(new float[] { 0.0f, 0.0f, 0.0f, 1.0f, 1.0f,
						0.0f, 1.0f, 1.0f },
						loadTexture(gl, direction));
			}

			if (direction == Tunnel.RIGHT) {
				j += SPEED;
			} else if (direction == Tunnel.LEFT) {
				j -= SPEED;
			} else if (direction == Tunnel.BOTTOM) {
				i += SPEED;
			} else if (direction == Tunnel.TOP) {
				i -= SPEED;
			}
		}

		private short nextStep(int iAnt, int jAnt) {
			int dist = Integer.MAX_VALUE;
			int newDist;
			short direction = 0;

			if ((maze.getGraph()[i][j] & Tunnel.TOP) > 0) {
				newDist = distanceGraph[iAnt - 1][jAnt];
				if (newDist < dist) {
					dist = newDist;
					direction = Tunnel.TOP;
				}
			}
			if ((maze.getGraph()[i][j] & Tunnel.BOTTOM) > 0) {
				newDist = distanceGraph[iAnt + 1][jAnt];
				if (newDist < dist) {
					dist = newDist;
					direction = Tunnel.BOTTOM;
				}
			}
			if ((maze.getGraph()[i][j] & Tunnel.RIGHT) > 0) {
				newDist = distanceGraph[iAnt][jAnt + 1];
				if (newDist < dist) {
					dist = newDist;
					direction = Tunnel.RIGHT;
				}
			}
			if ((maze.getGraph()[i][j] & Tunnel.LEFT) > 0) {
				newDist = distanceGraph[iAnt][jAnt - 1];
				if (newDist < dist) {
					dist = newDist;
					direction = Tunnel.LEFT;
				}
			}
			return direction;
		}
	}

	public class TongueObserver implements Tongue.MoveListener {

		SoundListener soundListener;

		public TongueObserver(SoundListener soundListener) {
			this.soundListener = soundListener;
		}

		@Override
		public void notify(short i, short j) {
			tongueRoute[i][j] = 1; // FIXME usado no flood fill, achar
									// alternativa.
			precalculateDistances();

			for (int k = 0; k < size(); k++) {
				Ant ant = (Ant) get(k);

				if (ant.getI() == i && ant.getJ() == j) {
					this.soundListener.playCatchAnt();
					dieAntDie(ant);
					break;
				}
			}
		}

		private void dieAntDie(Ant ant) {
			deadAnts.add(ant);
			remove(ant);
		}
	}

}
